/*
    Sox Lib By Luke Graham
*/

#include "sox.h"


void soxInitialize()
{
    #ifdef WIN32
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2),&wsaData);
    #endif
}

void soxCleanup()
{
    #ifdef WIN32
    WSACleanup();
    #endif
}

int soxLastError()
{
    #ifdef WIN32
    return WSAGetLastError();
    #else
    return errno;
    #endif
}

SoxHandle soxCreateTcpSocket()
{
    return socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
}

SoxHandle soxCreateUdpSocket()
{
    return socket(AF_INET, SOCK_DGRAM, 0);
}

unsigned int soxIpAddress(const char* ip)
{
    return inet_addr(ip);
}

unsigned int soxResolveHost(const char* hostName)
{
    sockaddr_in addr;
    hostent* hostEntry;

    if((hostEntry = gethostbyname(hostName)) == NULL)
        return 0;

    addr.sin_addr = *((in_addr*)*hostEntry->h_addr_list);
    return addr.sin_addr.s_addr;
}

const char* soxIpString(unsigned int ip)
{
    in_addr addr;
    addr.s_addr = ip;
    return inet_ntoa(addr);
}

unsigned int soxTcpIp(SoxHandle socketId)
{
    struct sockaddr_in addr;
    int addrSize = sizeof(addr);
    if(getsockname(socketId, (sockaddr*)&addr, &addrSize) == 0)
        return addr.sin_addr.s_addr;
    return 0;
}

void soxCloseSocket(SoxHandle socketId)
{
    #ifdef WIN32
    closesocket(socketId);
    #else
    close(socketId);
    #endif
}

void soxShutdownSocket(SoxHandle socketId)
{
    shutdown(socketId, 1);
}

void soxBlockSocket(SoxHandle socketId)
{
    unsigned long opt = 0;
	ioctlsocket(socketId, FIONBIO, &opt);
}

void soxUnblockSocket(SoxHandle socketId)
{
    unsigned long opt = 1;
	ioctlsocket(socketId, FIONBIO, &opt);
}

bool soxTcpConnect(SoxHandle socketId, unsigned int ipAddress, int port)
{
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = ipAddress;
    addr.sin_port = htons((unsigned short)port);

    if(connect(socketId, (sockaddr*)&addr, sizeof(addr)) == -1)
        return false;

    return true;
}

int soxTcpWrite(SoxHandle socketId, const char* data, size_t len)
{
    return send(socketId, data, len, 0);
}

int soxTcpWriteString(SoxHandle socketId, const char* str)
{
    return send(socketId, str, strlen(str), 0);
}

int soxTcpRead(SoxHandle socketId, char* out, size_t outLen)
{
    return recv(socketId, out, outLen, 0);
}

char* soxTcpReadLine(SoxHandle socketId, char* out, size_t outLen)
{
    //Peek the socket data
    int size = recv(socketId, out, outLen - 1, MSG_PEEK);

    if(size > 0)
    {
        //Check for an end of line character
        int recvCount = 0;
        while(recvCount < size)
        {
            if(out[recvCount++] == '\n')
                break;
        }

        //Receive the line
        size = recv(socketId, out, recvCount, 0);
        if(size > 0)
        {
            //Trim line
            while(size > 0)
            {
                if(out[size - 1] < ' ')
                    out[--size] = 0;
                else break;
            }
            out[size] = 0;
            return out;
        }
    }
    return NULL;
}

int soxTcpPeek(SoxHandle socketId, char* out, size_t outLen)
{
    return recv(socketId, out, outLen, MSG_PEEK);
}

bool soxTcpConnected(SoxHandle socketId)
{
    fd_set set;
    struct timeval timeout;
    FD_ZERO(&set);
    memset(&timeout, 0, sizeof(timeout));
    FD_SET(socketId, &set);
    return select(0, NULL, &set, NULL, &timeout) > 0;
}

void soxEnableNagle(SoxHandle socketId)
{
    int opt = 0;
    setsockopt(socketId, IPPROTO_TCP, TCP_NODELAY,(char*)&opt, sizeof(opt));
}

void soxDisableNagle(SoxHandle socketId)
{
    int opt = 1;
    setsockopt(socketId, IPPROTO_TCP, TCP_NODELAY,(char*)&opt, sizeof(opt));
}

bool soxBindUdpPort(SoxHandle socketId, int port)
{
    sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(port);

    if(bind(socketId,(sockaddr *)&addr, sizeof(sockaddr_in)) == -1)
		return false;

	return true;
}

int soxUdpWrite(SoxHandle socketId, const char* data, size_t len, unsigned int ipAddress, int port)
{
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = ipAddress;
    return sendto(socketId, data, len, 0, (sockaddr *)&addr, sizeof(sockaddr_in));
}

int soxUdpRead(SoxHandle socketId, char* out, size_t outLen)
{
    sockaddr_in addr;
    int addrSize = sizeof(addr);
    return recvfrom(socketId, out, outLen, 0, (sockaddr *)&addr, &addrSize);
}

int soxUdpPeek(SoxHandle socketId, char* out, size_t outLen)
{
    sockaddr_in addr;
    int addrSize = sizeof(addr);
    return recvfrom(socketId, out, outLen, MSG_PEEK, (sockaddr *)&addr, &addrSize);
}

bool soxTcpListen(SoxHandle socketId, int port)
{
    sockaddr_in addr;
    addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htons(INADDR_ANY);
	addr.sin_port = htons((short)port);

	if(bind(socketId, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1)
        return false;

    return listen(socketId, 50) != -1;
}

SoxHandle soxTcpAccept(SoxHandle socketId)
{
    sockaddr_in addr;
    int addrSize = sizeof(addr);
    return accept(socketId, (sockaddr *)&addr, &addrSize);
}

void soxUnlockPort(SoxHandle socketId)
{
    int on = 1;
    setsockopt(socketId, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
}
#ifdef __cplusplus

namespace sox
{
    SoxPoller::SoxPoller()
    {
        FD_ZERO(&readSet);
        FD_ZERO(&writeSet);
        FD_ZERO(&errorSet);
        range = 0;
        options = 0;
    }

    void SoxPoller::AddSocket(const Socket* s, int options)
    {
        SoxHandle socketId = s->GetSocket();
        this->options |= options;

        if(options & POLL_READ)
            FD_SET(socketId, &readSet);

        if(options & POLL_WRITE)
            FD_SET(socketId, &writeSet);

        if(options & POLL_ERROR)
            FD_SET(socketId, &errorSet);

        if(socketId > range)
            range = socketId;
    }

    bool SoxPoller::IsSet(const Socket* s, int option)
    {
        SoxHandle socketId = s->GetSocket();
        if(option == POLL_READ)
            return FD_ISSET(socketId, &readSet);

        if(option == POLL_WRITE)
            return FD_ISSET(socketId, &writeSet);

        if(option == POLL_ERROR)
            return FD_ISSET(socketId, &errorSet);

        return false;
    }

    int SoxPoller::Poll(int timeOut)
    {
        struct timeval timev;
        timev.tv_sec = 0;
        timev.tv_usec = timeOut;

        return select(range + 1, &readSet, &writeSet, &errorSet, (timeOut >= 0 ? &timev : NULL));
    }
}
#endif
